package com.util;

import com.util.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.omg.CORBA.SystemException;
import org.apache.commons.beanutils.BeanUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * Created by SkinApe on 2021/9/3.
 */
public class BeanConvertUtils {
    private static Pattern linePattern = Pattern.compile("_(\\w)");

    /** 下划线转驼峰 */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /** 驼峰转下划线,效率比上面高 */
    public static String humpToLine2(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * Map 下划线转 驼峰
     * @param source
     * @return
     */
    public static Map<String,Object> lineToHumpMap(Map<String, Object> source){
        Map<String,Object> result=new HashMap<>();
        if (source!=null){
            for (String key : source.keySet()) {
                result.put(lineToHump(key),source.get(key));
            }
        }
        return result;
    }

    /**
     * ListMap 下划线转 驼峰
     * @param sourceList
     * @return
     */
    public static List<Map<String,Object>> lineToHumpMapList(List<Map<String, Object>> sourceList){
        List<Map<String,Object>> resultList=new ArrayList<>();

        for (Map<String,Object> source:sourceList){
            resultList.add(lineToHumpMap(source));

        }
        return resultList;
    }

    public static <T> T sourceToTarget(Object source, Class<T> target) {

        T instance = null;

        try {
            instance = target.newInstance();
            if(source != null) {
                BeanUtils.copyProperties(instance, source);
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static <T> T sourceToTargetIgnoreCase(Object source, Class<T> target) {
        try {
            T instance = target.newInstance();
            if(source != null) {
                Field[] sourceDeclaredFields = source.getClass().getDeclaredFields();
                Field[] var4 = sourceDeclaredFields;
                int var5 = sourceDeclaredFields.length;

                for(int var6 = 0; var6 < var5; ++var6) {
                    Field sourceField = var4[var6];
                    if(!Modifier.isStatic(sourceField.getModifiers())) {
                        String sourceFieldName = sourceField.getName();

                        try {
                            Object sourceFieldValue = getFiledValue(source, sourceFieldName);
                            if(sourceFieldValue != null) {
                                injectTargetField(instance, sourceFieldName, sourceFieldValue, sourceField.getType());
                            }
                        } catch (InvocationTargetException var10) {
                            ;
                        }
                    }
                }
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T sourceMapToTarget(Map<String, Object> sourceMap, Class<T> target) {
        try {
            T instance = target.newInstance();
            try {
                BeanUtils.populate(instance, MapUtils.underlineMapConvertCamelMap(sourceMap));
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> List<T> sourceListToTargetList(List<?> sourceList, Class<T> target) {
        return (List)sourceList.stream().map((o) -> {
            return sourceToTarget(o, target);
        }).collect(Collectors.toList());
    }

    public static <T> List<T> sourceListToTargetListIgnoreCase(List<?> sourceList, Class<T> target) {
        return (List)sourceList.stream().map((o) -> {
            return sourceToTargetIgnoreCase(o, target);
        }).collect(Collectors.toList());
    }

    public static <T> Set<T> sourceSetToTargetSet(Set<?> sourceSet, Class<T> target) {
        return (Set)sourceSet.stream().map((o) -> {
            return sourceToTarget(o, target);
        }).collect(Collectors.toSet());
    }

    public static <T> Set<T> sourceSetToTargetSetIgnoreCase(Set<?> sourceSet, Class<T> target) {
        return (Set)sourceSet.stream().map((o) -> {
            return sourceToTargetIgnoreCase(o, target);
        }).collect(Collectors.toSet());
    }

    public static <T> List<T> sourceMapListToTargetList(List<Map<String, Object>> sourceMapList, Class<T> target) {
        return (List)sourceMapList.stream().map((sourceMap) -> {
            return sourceMapToTarget(sourceMap, target);
        }).collect(Collectors.toList());
    }

    private static String getFirstCharCase(String value) {
        return value.substring(0, 1).toUpperCase() + value.substring(1);
    }

    private static <T> void injectTargetField(T instance, String sourceFieldName, Object sourceFieldValue, Class<?> filedValueType) throws InvocationTargetException, IllegalAccessException {
        Field[] targetDeclaredFields = getInstanceAllFields(instance);
        Field[] var5 = targetDeclaredFields;
        int var6 = targetDeclaredFields.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Field targetField = var5[var7];
            if(!Modifier.isStatic(targetField.getModifiers())) {
                String targetFieldName = targetField.getName();
                if(StringUtils.equalsIgnoreCase(sourceFieldName, targetFieldName)) {
                    setFiledValue(instance, targetFieldName, sourceFieldValue, filedValueType);
                }
            }
        }

    }

    private static <T> Field[] getInstanceAllFields(T instance) {
        Class clazz = instance.getClass();

        ArrayList fieldList;
        for(fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()) {
            fieldList.addAll(new ArrayList(Arrays.asList(clazz.getDeclaredFields())));
        }

        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    private static void setFiledValue(Object object, String filedName, Object filedValue, Class<?> filedValueType) throws InvocationTargetException, IllegalAccessException {
        Method method = getFiledSetMethod(object, filedName, filedValueType);
        if(method == null) {
            if(StringUtils.startsWith(filedName, "is")) {
                filedName = filedName.substring(2);
            }

            method = getFiledSetMethod(object, filedName, filedValueType);
        }

        if(method != null) {
            method.invoke(object, new Object[]{filedValue});
        }

    }

    private static Object getFiledValue(Object object, String filedName) throws InvocationTargetException, IllegalAccessException {
        Method method = getFiledGetMethod(object, "get", filedName);
        if(method == null) {
            method = getFiledGetMethod(object, "is", filedName);
        }

        if(method == null) {
            method = getFiledGetMethod(object, "", filedName);
        }

        return method == null?null:method.invoke(object, new Object[0]);
    }

    private static Method getFiledGetMethod(Object object, String prefix, String filedName) {
        try {
            return "".equals(prefix)?object.getClass().getMethod(filedName, new Class[0]):object.getClass().getMethod(prefix + getFirstCharCase(filedName), new Class[0]);
        } catch (NoSuchMethodException var4) {
            return null;
        }
    }

    private static Method getFiledSetMethod(Object object, String filedName, Class<?> filedValueType) {
        try {
            return object.getClass().getMethod("set" + getFirstCharCase(filedName), new Class[]{filedValueType});
        } catch (NoSuchMethodException var4) {
            return null;
        }
    }
}
